#include "stdafx.h"
#include <boost/test/auto_unit_test.hpp>
#include "bmock.hpp"
#include <boost/utility.hpp>

static int f(int x, int& y, int* z) {
	STATIC_MOCK(3,(IN(x),OUT(y),OUT(z)));	
}
static void g(int& y, int* z) {
	STATIC_MOCK_VOID(2,(OUT(y),OUT(z)));
}

struct update_arguments {
	update_arguments()
		:y_expected(y)
		,z_expected(z)
		,y_actual(0)
		,z_actual(0)
		,r_actual(0)
	{}

	static const int y = 39;
	static const int z = 57;
	static const int x = 25;
	static const int r = 197;

	int y_expected;
	int z_expected;
	int y_actual;
	int z_actual;
	int r_actual;
};

const int update_arguments::y;
const int update_arguments::z;
const int update_arguments::x;
const int update_arguments::r;


BMOCK_TEST(update_arguments,test_full) {
	BOOST_CHECK_NO_THROW(BMOCK_EXPECT_RETURN(r,f(x,y_expected,&z_expected)));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(r_actual = f(x,y_actual,&z_actual));
	BOOST_CHECK_EQUAL(r,r_actual);
	BOOST_CHECK_EQUAL(y,y_actual);
	BOOST_CHECK_EQUAL(z,z_actual);
	BMOCK_VERIFY;
}

BMOCK_TEST(update_arguments,test_partial) {
	BOOST_CHECK_NO_THROW(BMOCK_EXPECT(g(y_expected,&z_expected)));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(g(y_actual,&z_actual));
	BOOST_CHECK_EQUAL(y,y_actual);
	BOOST_CHECK_EQUAL(z,z_actual);
	BMOCK_VERIFY;
}

BMOCK_TEST(update_arguments,test_multiple) {
	BOOST_CHECK_NO_THROW(BMOCK_EXPECT(g(y_expected,&z_expected)));
	++y_expected;
	++z_expected;
	BOOST_CHECK_NO_THROW(BMOCK_EXPECT(g(y_expected,&z_expected)));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(g(y_actual,&z_actual));
	BOOST_CHECK_EQUAL(y,y_actual);
	BOOST_CHECK_EQUAL(z,z_actual);
	BOOST_CHECK_NO_THROW(g(y_actual,&z_actual));
	BOOST_CHECK_EQUAL(y+1,y_actual);
	BOOST_CHECK_EQUAL(z+1,z_actual);
	BMOCK_VERIFY;
}

static void h(unsigned char& c) {
	STATIC_MOCK_VOID(1,(OUT(c)));
}
static void h2(char& c) {
	STATIC_MOCK_VOID(1,(OUT(c)));
}


struct tester{
	tester():uc_expected(ucc),c_expected(cc){
	}
	static const unsigned char ucc = 'c';
	static const char cc = 'd';
	
	unsigned char uc_expected;	
	char c_expected;
	unsigned char uc_actual;	
	char c_actual;
};


BMOCK_TEST(tester, test_unsigned_char_referece) {
	BMOCK_EXPECT(h(uc_expected));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(h(uc_actual));
	BOOST_CHECK_EQUAL(uc_expected,uc_actual);
	BMOCK_VERIFY;
}

BMOCK_TEST(tester, test_char_referece) {
	BMOCK_EXPECT(h2(c_expected));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(h2(c_actual));
	BOOST_CHECK_EQUAL(c_expected,c_actual);
	BMOCK_VERIFY;
}

BMOCK_TEST(tester, test_unsigned_char_reference_zero) {
	uc_expected = 0;

	BMOCK_EXPECT(h(uc_expected));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(h(uc_actual));
	BOOST_CHECK_EQUAL(uc_expected,uc_actual);
	BMOCK_VERIFY;
}

BMOCK_TEST(tester, test_char_referece_zero) {
	c_expected = 0;

	BMOCK_EXPECT(h2(c_expected));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(h2(c_actual));
	BOOST_CHECK_EQUAL(c_expected,c_actual);
	BMOCK_VERIFY;
}

struct test_double_pointer
{	
	const int DEF_VAL;
	
	test_double_pointer(): DEF_VAL(3), x_(DEF_VAL) {}
	void pod_type(int** x);
	void non_pod_type(test_double_pointer** tdp);

	int x_;
};

void test_double_pointer::pod_type(int** x)
{
	STATIC_MOCK_VOID(1, (OUT(x)));
}

void test_double_pointer::non_pod_type(test_double_pointer** tdp)
{
	STATIC_MOCK_VOID(1, (OUT(tdp)));
}

BMOCK_TEST(test_double_pointer, test_pod)
{
	int x = test_double_pointer::DEF_VAL;
	int* px_expected = &x;
	int* px_actual = NULL;

	BMOCK_EXPECT(pod_type(&px_expected));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(pod_type(&px_actual));
	BMOCK_VERIFY;

	BOOST_CHECK_EQUAL(px_expected, px_actual);
	BOOST_CHECK_EQUAL(&x, px_actual);
	BOOST_CHECK_EQUAL(*px_actual, x);
	BOOST_CHECK_EQUAL(*px_actual, DEF_VAL);
}

BMOCK_TEST(test_double_pointer, test_non_pod)
{
	test_double_pointer tdp;
	test_double_pointer* ptdp_expected = &tdp;
	test_double_pointer* ptdp_actual = NULL;

	BMOCK_EXPECT(non_pod_type(&ptdp_expected));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(non_pod_type(&ptdp_actual));
	BMOCK_VERIFY;

	BOOST_CHECK_EQUAL(ptdp_expected, ptdp_actual);
	BOOST_CHECK_EQUAL(&tdp, ptdp_actual);
	BOOST_CHECK_EQUAL(ptdp_actual->x_, tdp.x_);
	BOOST_CHECK_EQUAL(ptdp_actual->x_, DEF_VAL);
}

struct test_non_copy : boost::noncopyable
{
	test_non_copy():x(0){}
	int x;
};

struct test_non_copy_fixture {};

static void f_non_copy(test_non_copy* tnc)
{
	STATIC_MOCK_VOID(1, (RAW_MEM(OUT, tnc, 1)));
}

BMOCK_TEST(test_non_copy_fixture, test_non_copyable_OUT)
{
	test_non_copy tnc_expected, tnc_actual;
	tnc_expected.x = 5;
	
	BMOCK_EXPECT(f_non_copy(bmock::out_raw_mem(&tnc_expected,1)));
	BMOCK_REPLAY;
	f_non_copy(&tnc_actual);
	BMOCK_VERIFY;
	BOOST_CHECK_EQUAL(tnc_expected.x, tnc_actual.x);
}




